package com.fsmv.brainfuck2;

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.String;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.TabActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.os.SystemClock;
import android.text.InputFilter;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;

public class main extends TabActivity {
	Bundle extras;
	EditText code;
	AlertDialog.Builder[] alerts = new AlertDialog.Builder[20];
	int alertIndex;
	int bytes[] = new int[500];
	int index = 0;
    String fileData = "";
    boolean ran;
    boolean printdata;
    TextView textview;
    String stdout = "";
    int inputPos[] = new int[20];
	char inputValues[] = new char[20];
	int inputs = 0;
	int inputIndex = 0;
	File root;
	String fileName;
	TextView nameView;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        code = (EditText)findViewById(R.id.codeBox); 
        nameView = (TextView)findViewById(R.id.nameView);
        root = new File("/sdcard/brainfuck/");
        TabHost mTabHost = getTabHost();
        
        mTabHost.addTab(mTabHost.newTabSpec("Edit").setIndicator("Edit", getResources().getDrawable(R.drawable.ic_tab_edit)).setContent(R.id.main_layout));
        mTabHost.addTab(mTabHost.newTabSpec("Run").setIndicator("Run", getResources().getDrawable(R.drawable.ic_tab_run)).setContent(R.id.run));
        
        mTabHost.setCurrentTab(0);
        
        mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener(){
        	@Override
        	public void onTabChanged(String tabid) {
        		if(tabid.equals("Run"))
        			run();
        		else if(tabid.equals("Edit"))
        			edit();
        	}
        });
    }
    
    @Override
    public void onPause(){
    	super.onPause();
    	if(fileName == null)
    		saveAs("temp.bf");
    	else
    		saveAs(fileName);
    }
    
    @Override
    public void onResume(){
    	File temp = new File(root, "temp.bf");
    	if(temp.exists())
    		loadFile(root, "temp.bf");
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent){
    	Log.w("result Recieved", "request code: " + requestCode + ", result code: " + resultCode);
    	super.onActivityResult(requestCode, resultCode, intent);
    	if (requestCode == 0 && resultCode == Activity.RESULT_OK){
    		extras = intent.getExtras();
    		if(extras != null){
    			if(intent.hasExtra("selectedDir"))
    				root = new File(new File(extras.getString("selectedDir")).getParent());
    			else
    				Log.w("Result error:", "selectedDir not recieved");
    			if(intent.hasExtra("selectedFile"))
    				fileName = extras.getString("selectedFile");
    			else
    				Log.w("Result error:", "selectedFile not recieved");
    			loadFile(root, fileName);
    		}
    	}
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    	Intent intent = new Intent();
        switch (item.getItemId()) {
        case R.id.ascii:
        	intent.setClass(getApplicationContext(), Ascii.class);
        	intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            getApplicationContext().startActivity(intent);
            break;
        case R.id.open:
        	intent.setClass(this, FileManager.class);
        	startActivityForResult(intent, 0);
        	break;
        case R.id.help:
        	intent.setClass(this, Help.class);
        	intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            getApplicationContext().startActivity(intent);
            break;
        case R.id.save:
        	AlertDialog.Builder alert = new AlertDialog.Builder(this).setCancelable(false);
			
		    alert.setTitle("Save as...");
		    alert.setMessage("Enter a filename (with extention ex: .bf)");
	
		    final EditText input = new EditText(this);
		    alert.setView(input);
		    alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
		    	public void onClick(DialogInterface dialog, int whichButton) {
		    		if(input.getText().toString().equals("")){
		    			Toast.makeText(getApplicationContext(), "ERROR: no file name entered using old name", Toast.LENGTH_LONG);
		    			saveAs(fileName);
		    		}else
		    			saveAs(input.getText().toString());
		    	}
		   });
		    alert.show();
        	break;
        case R.id.delete:
        	OnClickListener okButtonListener = new OnClickListener(){
                public void onClick(DialogInterface arg0, int arg1) {
                	deleteCurrentFile();
                }
              };
        	
              OnClickListener cancelButtonListener = new OnClickListener() {
                  public void onClick(DialogInterface arg0, int arg1) {
                    //do nothing
                  }
                };
                
        	  new AlertDialog.Builder(this)
              .setTitle("Confirmation")
              .setMessage("Are you sure you want to delete " + root.getPath() + "/" + fileName)
              .setPositiveButton("Yes", okButtonListener)
              .setNegativeButton("No", cancelButtonListener)
              .show();
        	break;
        default:
            return super.onOptionsItemSelected(item);
        }
		return super.onOptionsItemSelected(item);
    }
    
    public void edit(){
    	code.setText(fileData);
    	stdout = "";
    }
    
    public void deleteCurrentFile(){
    	if(fileName != null){
    		File f = new File(root, fileName);
    		if(f.exists()){
            	if(f.canWrite()){
            		boolean success = f.delete();
            		if (!success)
            			Toast.makeText(this, "ERROR: File deletion failed", Toast.LENGTH_LONG);
            		else
            			code.setText("");
            	}else
            		Toast.makeText(this, "ERROR: File is write protected", Toast.LENGTH_LONG);
            }else
            	Toast.makeText(this, "ERROR: File does not exist", Toast.LENGTH_LONG);
    	}else
    		Toast.makeText(this, "ERROR: File is null", Toast.LENGTH_LONG);
    }
    
    public boolean loadFile(File rootDir, String name){
    	if(rootDir != null && name != null){
    		try {
    			File file = new File(rootDir, name);
    			FileInputStream fis = new FileInputStream(file);
    			BufferedInputStream bis = new BufferedInputStream(fis);
    			DataInputStream dis = new DataInputStream(bis);
    			String fileData = "";
    			if(file.exists()){
    				Log.d("DEBUG:", "file exits");
    				if(file.canRead()){
    					Log.d("DEBUG:", "can read");
    					while(dis.available() > 0){
    						fileData += dis.readLine() + "\n";
    					}
    					Log.d("DEBUG:", "fileData set");
    				}
    			}else{
    				Log.e("Load File error:", "file does not exist!");
    			}
    			fis.close();
    			dis.close();
    			bis.close();
    			code.setText(fileData);
    			nameView.setText(file.getPath());
    			return true;
    		} catch (IOException e) {
    			Log.e("Load file error", "Could not read file " + e.getMessage());
    			return false;
    		}
    	}else{
    		Log.e("Load file error", "File is null");
    		return false;
    	}
    }
    
    public boolean saveFile(File rootDir, String name){
    	if(rootDir == null || name == null){
    		AlertDialog.Builder alert = new AlertDialog.Builder(this).setCancelable(false);
			
		    alert.setTitle("Save as...");
		    alert.setMessage("Enter a filename (with extention ex: .bf)");
	
		    final EditText input = new EditText(this);
		    alert.setView(input);
		    alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
		    	public void onClick(DialogInterface dialog, int whichButton) {
		    		if(input.getText().toString().equals("")){
		    			Toast.makeText(getApplicationContext(), "ERROR: no file name entered using old name", Toast.LENGTH_LONG);
		    			saveAs(fileName);
		    		}else
		    			saveAs(input.getText().toString());
		    	}
		   });
		    alert.show();
		    return false;
    	}else{
    	try {
		    File file = new File(rootDir, name);
		    if(!file.exists()){
			   file.createNewFile();
		    }
		    if (file.canWrite()){
		        
		        FileWriter writer = new FileWriter(file);
		        BufferedWriter out = new BufferedWriter(writer);
		        out.write(code.getText().toString());
		        out.close();
		        nameView.setText(file.getPath());
		    }
		    return true;
		} catch (IOException e) {
		    Log.e("Save file", "Could not write file " + e.getMessage());
		    return false;
		}
    	}
    }
    
    public void saveAs(String name){
    	if(name == null || name == "")
    		Toast.makeText(this, "ERROR: File name null", Toast.LENGTH_LONG);
    	else{
    		fileName = name;
    		saveFile(root, fileName);
    	}
    }
    
    public void run(){
    	fileData = code.getText().toString();
    	inputs();
    }
    
    public void ExecuteCode(){
    	String stdoutfin = "\nFINAL OUTPUT: ";
    	String code = fileData;
    	int errors = 0;	
    	int loop[] = new int[20];
    	for(int i=0; i<20; i++){
    		loop[i] = -1;
    	}
    	int loops = -1;
    	bytes = new int[500];
    	index = 0;
		    for(int i=0; i < code.length(); i++){
		    	switch(code.charAt(i)){
		    	case '+':
		    		bytes[index]++;
		    		if(printdata)
		    			stdout("+ Current Byte: " + Integer.toString(bytes[index]) + "\n");
		    		break;
		    	case '-':
		    		bytes[index]--;
		    		if(printdata)
		    			stdout("- Current Byte: " + Integer.toString(bytes[index]) + "\n");
		    		break;
		    	case '<':
		    		if(index > 0)
		    			index--;
		    		else
		    			Toast.makeText(this, "Warning: Index is already at zero.", Toast.LENGTH_LONG).show();
		    		if(printdata)
		    			stdout("< Current Index: " + Integer.toString(index) + "\n");
		    		break;
		    	case '>':
		    		if(index <= 500)
		    			index++;
		    		else
		    			Toast.makeText(this, "Warning: Maximum index reached.", Toast.LENGTH_LONG).show();
		    		if(printdata)
		    			stdout("> Current Index: " + Integer.toString(index) + "\n");
		    		break;
		    	case ']':
		    		if(loops == -1){
		    			Toast.makeText(this, "ERROR: Close bracket before an open bracket!", Toast.LENGTH_LONG).show();
		    			errors++;
		    		}else{
		    			if(bytes[index] == 0){
		    				loop[loops] = -1;
		    				loops--;
		    				if(printdata)
		    					stdout("] Loop ended\n");
		    			}else if(bytes[index] > 255){ //Stop if you've gone too high, indicating an infinite loop
		    				Toast.makeText(this, "ERROR: ASCII ends at 255 a byte has gone to 256 infinite loop", Toast.LENGTH_LONG);
		    				errors++;
		    			}else{
		    				i = loop[loops];
		    				if(printdata)
		    					stdout("] Skipping back to: " + Integer.toString(i) + "\n");
		    			}
		    		}
		    		break;
		    	case '[':
		    		loops++;
		    		if(loops > 20){
		    			Toast.makeText(this, "ERROR: This implementations only allows for 20 nested loops. If you want more email me. fsmv@fsmv.co.cc", Toast.LENGTH_LONG);
		    			errors++;
		    		}
		    		loop[loops] = i;
		    		if(printdata)
		    			stdout("[ Opened loop\n");
		    		break;
		    	case '.':
		    		if(printdata){
		    			stdout(". OUTPUT: " + Character.toString((char)bytes[index]) + "\n");
		    			stdoutfin += Character.toString((char)bytes[index]);
		    		}else
		    			stdout(Character.toString((char)bytes[index]));
		    		break;
		    	case ',':
		    		if(inputs-1 >= 0){
		    			if(inputValues[inputs] == (char) 0){
		    				Toast.makeText(this, "ERROR: No input entered!", Toast.LENGTH_LONG).show();
		    				errors++;
		    			}
		    			inputs--;
		    			setByte(inputValues[inputs]);
		    			if(printdata)
		    				stdout(", Input given: " + Character.toString(inputValues[inputs]) + " Byte value is: " + Integer.toString(bytes[index]) + "\n");
		    		}
		    		break;
		    	case '*':
		    		String temp = "\nCurrent index: " + Integer.toString(index);
		    		temp += "\nCurrent Byte values: ";
		    		temp += "0 - " + Integer.toString(bytes[0]) + ", ";
		    		temp += "1 - " + Integer.toString(bytes[1]) + ", ";
		    		for(int a=2; a<500; a++){
		    			if(bytes[a] == 0){
		    				break;
		    			}else if(bytes[a] != 0){
		    				temp += Integer.toString(a) + " - " + Integer.toString(bytes[a]) + ", ";
		    			}
		    		}
		    		temp += "\n\n";
		    		stdout(temp);
		    		break;
		    	case '?':
		    		printdata = true;
		    		break;
		    }
		    if(errors > 0)
		    	break;
	    }
	    if(printdata)
			stdout(stdoutfin);
    }
    
    public void stdout(String str){
    	textview = (TextView)findViewById(R.id.run);
    	textview.setVerticalScrollBarEnabled(true);
    	textview.setMovementMethod(ScrollingMovementMethod.getInstance());
    	stdout += str;
    	textview.setText(stdout);
    }
    
    public void setByte(char value){
    	bytes[index] = (int)value;
    }
    
    public void setInput(char input){
    	inputValues[inputIndex] = input;
    	inputIndex--;
    	if(inputIndex == -1){
    		ExecuteCode();
    		inputs = 0;
        	inputIndex = -1;
    	}
    }
    
    public void inputs(){
    	for(int i=0; i < fileData.length(); i++){
    		if(fileData.charAt(i) == ','){
    			if(inputs >= 20){
    				Toast.makeText(this, "ERROR: This implementations only allows for 20 inputs. If you want more email me. fsmv@fsmv.co.cc", Toast.LENGTH_LONG);
    				break;
    			}
    			inputPos[inputs] = i;
    			inputs++;
    		}
    	}
    	for(int i=inputs-1; i>-1; i--){    		    	    	    	
	    	alerts[i] = new AlertDialog.Builder(this).setCancelable(false);
			
		    alerts[i].setTitle("Enter Character");
		    alerts[i].setMessage("Script is requesting input number " + Integer.toString(i+1));
	
		    final EditText input = new EditText(this);
		    InputFilter[] FilterArray = new InputFilter[1];
		    FilterArray[0] = new InputFilter.LengthFilter(1);
		    input.setFilters(FilterArray);
	
		    alerts[i].setView(input);
	
		    alerts[i].setPositiveButton("Ok", new DialogInterface.OnClickListener() {
		    	public void onClick(DialogInterface dialog, int whichButton) {
		    		String temp = input.getText().toString();
		    		if(temp.length() > 0)
		    			setInput(temp.charAt(0));
		    		else
		    			setInput((char) 0);
		    		nextAlert();
		    	}
		   });
    	}
    	alertIndex = inputs-1;
    	if(inputs == 0){
    		ExecuteCode();
    	}else{
    		inputIndex = inputs-1;
    		nextAlert();
    	}
    }
    
    public void nextAlert(){
    	if(alertIndex >= 0)
    		alerts[alertIndex].show();
    	alertIndex--;
    }
    
    public void buttonHandler(View view){
		switch(view.getId()) {
		case R.id.bs:
			code.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
			code.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
			break;
		case R.id.rt:
			code.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
			code.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
			break;
		case R.id.gt:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), ">", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.lt:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), "<", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.lb:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), "[", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.rb:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), "]", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.pl:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), "+", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.mn:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), "-", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.co:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), ",", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.pd:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), ".", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.qm:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), "?", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.st:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), "*", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.sp:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), " ", 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		case R.id.tb:
			code.dispatchKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), Character.toString((char) 9), 0, KeyEvent.FLAG_SOFT_KEYBOARD));
			break;
		}
	}
}